/* drivers/input/touchscreen/gt9xx.c
 *
 * 2010 - 2012 Goodix Technology.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be a reference
 * to you, when you are integrating the GOODiX's CTP IC into your system,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * Version:1.4
 * Author:andrew@goodix.com
 * Release Date:2012/12/12
 * Revision record:
 *      V1.0:2012/08/31,first Release
 *      V1.2:2012/10/15,modify gtp_reset_guitar,slot report,tracking_id & 0x0F
 *      V1.4:2012/12/12,modify gt9xx_update.c
 *
 */
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/pm.h>

#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/io.h>
#include <linux/gpio.h>

#include "gt9xx_ts.h"

static const char *goodix_ts_name = "gt9xx_ts";
static struct workqueue_struct *goodix_wq;
struct i2c_client *i2c_connect_client = NULL;
static u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};

static s8 gtp_i2c_test(struct i2c_client *client);
void gtp_reset_guitar(struct i2c_client *client, s32 ms);
void gtp_int_sync(s32 ms);

///////////////////////////////////////////////
#define CTP_NAME ("gt9xx_ts")

#define SCREEN_MAX_X (screen_max_x)
#define SCREEN_MAX_Y (screen_max_y)
#define PRESS_MAX (255)

static int screen_max_x = 0;
static int screen_max_y = 0;
static int revert_x_flag = 0;
static int revert_y_flag = 0;
static int exchange_x_y_flag = 0;
static u32 debug_mask = 0;

enum
{
        DEBUG_INIT = 1U << 0,
        DEBUG_SUSPEND = 1U << 1,
        DEBUG_INT_INFO = 1U << 2,
        DEBUG_X_Y_INFO = 1U << 3,
        DEBUG_KEY_INFO = 1U << 4,
        DEBUG_WAKEUP_INFO = 1U << 5,
        DEBUG_OTHERS_INFO = 1U << 6,
};

module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);

static const unsigned short normal_i2c[3] = {0x14, 0x5d, I2C_CLIENT_END};

struct ctp_config_info config_info = {
    .input_type = CTP_TYPE,
    .name = NULL,
    .int_number = 0,
};

static void goodix_resume_events(struct work_struct *work);
static struct workqueue_struct *goodix_wq;
static struct workqueue_struct *goodix_resume_wq;
static DECLARE_WORK(goodix_resume_work, goodix_resume_events);

/**
 * input_set_int_enable() - input set irq enable
 * Input_type:sensor type
 *      enable:
 * return value: 0 : success
 *               -EIO :  i/o err.
 */
int input_set_int_enable(enum input_sensor_type *input_type, u32 enable)
{
        int ret = -1;
        u32 irq_number = 0;
        void *data = NULL;

        switch (*input_type)
        {
        case CTP_TYPE:
                data = container_of(input_type,
                                    struct ctp_config_info, input_type);
                irq_number = gpio_to_irq(((struct ctp_config_info *)data)->int_number);
                break;
        case GSENSOR_TYPE:
                break;
        case LS_TYPE:
                data = container_of(input_type,
                                    struct sensor_config_info, input_type);
                irq_number = gpio_to_irq(((struct sensor_config_info *)data)->int_number);
                break;
        default:
                break;
        }

        if ((enable != 0) && (enable != 1))
                return ret;

        if (enable == 1)
                enable_irq(irq_number);
        else
                disable_irq_nosync(irq_number);

        return 0;
}
EXPORT_SYMBOL(input_set_int_enable);

/**
 * ctp_detect - Device detection callback for automatic device creation
 * return value:
 *                    = 0; success;
 *                    < 0; err
 */
static int ctp_detect(struct i2c_client *client, struct i2c_board_info *info)
{
        struct i2c_adapter *adapter = client->adapter;
        int ret = -1;

        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
        {
                printk("======ctp_detect ENODEV=====\n");
                return -ENODEV;
        }
        printk("%s: addr = %x\n", __func__, client->addr);

        ret = gtp_i2c_test(client);
        printk("detect ret %d\n", ret);
        if (!ret)
        {
                printk("%s:I2C connection might be something wrong \n", __func__);
                return -ENODEV;
        }
        else
        {
                strlcpy(info->type, CTP_NAME, I2C_NAME_SIZE);
                printk("======detect ok !=====\n");
                return 0;
        }
}

/**
 * ctp_print_info - sysconfig print function
 * return value:
 *
 */
void ctp_print_info(struct ctp_config_info info, int debug_level)
{
        printk("info.screen_max_x:%d\n", info.screen_max_x);
        printk("info.screen_max_y:%d\n", info.screen_max_y);
        printk("info.revert_x_flag:%d\n", info.revert_x_flag);
        printk("info.revert_y_flag:%d\n", info.revert_y_flag);
        printk("info.exchange_x_y_flag:%d\n", info.exchange_x_y_flag);
}

/**
 * ctp_wakeup - function
 *
 */
int ctp_wakeup(int status, int ms)
{
        printk("***CTP*** %s:status:%d,ms = %d\n", __func__, status, ms);

        return 0;
}

/*******************************************************
Function:
        Read data from the i2c slave device.

Input:
        client:	i2c device.
        buf[0]:operate address.
        buf[1]~buf[len]:read data buffer.
        len:operate length.

Output:
        numbers of i2c_msgs to transfer
*********************************************************/
s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{
        struct i2c_msg msgs[2];
        s32 ret = -1;
        s32 retries = 0;

        msgs[0].flags = !I2C_M_RD; // write
        msgs[0].addr = client->addr;
        msgs[0].len = GTP_ADDR_LENGTH;
        msgs[0].buf = &buf[0];

        msgs[1].flags = I2C_M_RD; // read
        msgs[1].addr = client->addr;
        msgs[1].len = len - GTP_ADDR_LENGTH;
        msgs[1].buf = &buf[GTP_ADDR_LENGTH];

        while (retries < 2)
        {
                ret = i2c_transfer(client->adapter, msgs, 2);
                if (ret == 2)
                {
                        break;
                }
                retries++;
        }
        if (retries >= 2)
        {
                printk("%s:I2C retry timeout, reset chip.", __func__);
        }

        return ret;
}

/*******************************************************
Function:
        write data to the i2c slave device.

Input:
        client:	i2c device.
        buf[0]:operate address.
        buf[1]~buf[len]:write data buffer.
        len:operate length.

Output:
        numbers of i2c_msgs to transfer.
*********************************************************/
s32 gtp_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
{
        struct i2c_msg msg;
        s32 ret = -1;
        s32 retries = 0;

        msg.flags = !I2C_M_RD;
        msg.addr = client->addr;
        msg.len = len;
        msg.buf = buf;

        while (retries < 2)
        {
                ret = i2c_transfer(client->adapter, &msg, 1);
                if (ret == 1)
                        break;
                retries++;
        }

        if (retries >= 2)
        {
                printk("%s:I2C retry timeout, reset chip.", __func__);
        }
        return ret;
}

/*******************************************************
Function:
        Disable IRQ Function.

Input:
        ts:	i2c client private struct.

Output:
        None.
*******************************************************/
void gtp_irq_disable(struct goodix_ts_data *ts)
{
        unsigned long irqflags;
        int ret;

        printk("%s ---start!---\n", __func__);
        spin_lock_irqsave(&ts->irq_lock, irqflags);

        if (!ts->irq_is_disable)
        {
                ts->irq_is_disable = 1;
                ret = input_set_int_enable(&(config_info.input_type), 0);
                if (ret < 0)
                {
                        printk("%s irq disable failed\n", goodix_ts_name);
                }
        }

        spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}

/*******************************************************
Function:
        Disable IRQ Function.

Input:
        ts:	i2c client private struct.

Output:
        None.
*******************************************************/
void gtp_irq_enable(struct goodix_ts_data *ts)
{
        unsigned long irqflags = 0;
        int ret;

        printk("%s ---start!---\n", __func__);
        spin_lock_irqsave(&ts->irq_lock, irqflags);

        if (ts->irq_is_disable)
        {
                ts->irq_is_disable = 0;
                ret = input_set_int_enable(&(config_info.input_type), 1);
                if (ret < 0)
                {
                        printk("%s irq enable failed\n", goodix_ts_name);
                }
        }

        spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}

/*******************************************************
Function:
        Touch down report function.

Input:
        ts:private data.
        id:tracking id.
        x:input x.
        y:input y.
        w:input weight.

Output:
        None.
*******************************************************/
static void gtp_touch_down(struct goodix_ts_data *ts, s32 id, s32 x, s32 y, s32 w)
{
        // printk("source data:ID:%d, X:%d, Y:%d, W:%d\n", id, x, y, w);
        if (1 == exchange_x_y_flag)
        {
                swap(x, y);
        }
        if (1 == revert_x_flag)
        {
                x = SCREEN_MAX_X - x;
        }
        if (1 == revert_y_flag)
        {
                y = SCREEN_MAX_Y - y;
        }

        // printk("report data:ID:%d, X:%d, Y:%d, W:%d\n", id, x, y, w);
        input_report_key(ts->input_dev, BTN_TOUCH, 1);
        input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
        input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
        input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
        input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
        input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);

        input_mt_sync(ts->input_dev);
}

/*******************************************************
Function:
        Touch up report function.

Input:
        ts:private data.

Output:
        None.
*******************************************************/
static void gtp_touch_up(struct goodix_ts_data *ts, s32 id)
{
        input_report_key(ts->input_dev, BTN_TOUCH, 0);
        input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
        input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
        input_mt_sync(ts->input_dev);
}

/*******************************************************
Function:
        Goodix touchscreen work function.

Input:
        work:	work_struct of goodix_wq.

Output:
        None.
*******************************************************/
static void goodix_ts_work_func(struct work_struct *work)
{
        u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
        u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
        u8 touch_num = 0;
        u8 finger = 0;
        static u16 pre_touch = 0;
        u8 *coor_data = NULL;
        s32 input_x = 0;
        s32 input_y = 0;
        s32 input_w = 0;
        s32 id = 0;
        s32 i = 0;
        int j = 0;
        s32 ret = -1;
        struct goodix_ts_data *ts = NULL;

        ts = container_of(work, struct goodix_ts_data, work);

        for (j = 0; j < 2; j++)
        {
                ret = gtp_i2c_read(ts->client, point_data, 12);
                if (ret < 0)
                {
                        printk("I2C transfer error. errno:%d\n ", ret);
                        goto exit_work_func;
                }
        }
        finger = point_data[GTP_ADDR_LENGTH];
        if ((finger & 0x80) == 0)
        {
                goto exit_work_func;
        }
        touch_num = finger & 0x0f;
        if (touch_num > GTP_MAX_TOUCH)
        {
                touch_num = 1;
        }

        if (touch_num > 1)
        {
                u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
                ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
                memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
        }
        // printk("pre_touch:%02x, finger:%02x.", pre_touch, finger);

        if (touch_num > 0)
        {
                for (i = 0; i < touch_num; i++)
                {
                        coor_data = &point_data[i * 8 + 3];

                        id = coor_data[0] & 0x0F;
                        input_x = coor_data[1] | coor_data[2] << 8;
                        input_y = coor_data[3] | coor_data[4] << 8;
                        input_w = coor_data[5] | coor_data[6] << 8;
                        gtp_touch_down(ts, id, input_x, input_y, input_w);
                }
        }
        else if (pre_touch)
        {
                printk("Touch Release!");
                gtp_touch_up(ts, 0);
        }
        pre_touch = touch_num;

        input_sync(ts->input_dev);

exit_work_func:
        if (!ts->gtp_rawdiff_mode)
        {
                ret = gtp_i2c_write(ts->client, end_cmd, 3);
                if (ret < 0)
                {
                        printk("I2C write end_cmd  error!");
                }
        }
}

/*******************************************************
Function:
        External interrupt service routine.

Input:
        irq:	interrupt number.
        dev_id: private data pointer.

Output:
        irq execute status.
*******************************************************/
irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{
        struct goodix_ts_data *ts = (struct goodix_ts_data *)dev_id;

        queue_work(goodix_wq, &ts->work);
        msleep(20);

        return IRQ_HANDLED;
}

/*******************************************************
Function:
        GTP initialize function.

Input:
        ts:	i2c client private struct.

Output:
        Executive outcomes.0---succeed.
*******************************************************/
static s32 gtp_init_panel(struct goodix_ts_data *ts)
{
        s32 ret = -1;

        if (ts->gtp_cfg_len == 0)
        {
                ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
        }
        ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH);
        if (ret < 0)
        {
                printk("GTP read resolution & max_touch_num failed, use default value!");
                ts->abs_x_max = GTP_MAX_WIDTH;
                ts->abs_y_max = GTP_MAX_HEIGHT;
                ts->int_trigger_type = GTP_INT_TRIGGER;
        }

        ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC];
        ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2];
        ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
        if ((!ts->abs_x_max) || (!ts->abs_y_max))
        {
                printk("GTP resolution & max_touch_num invalid, use default value! w:%d, H:%d\r\n", ts->abs_x_max, ts->abs_y_max);
                ts->abs_x_max = GTP_MAX_WIDTH;
                ts->abs_y_max = GTP_MAX_HEIGHT;
        }
        printk("X_MAX = %d,Y_MAX = %d,TRIGGER = 0x%02x", ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);

        return 0;
}

/*******************************************************
Function:
        Read goodix touchscreen version function.

Input:
        client:	i2c client struct.
        version:address to store version info

Output:
        Executive outcomes.0---succeed.
*******************************************************/
s32 gtp_read_version(struct i2c_client *client, u16 *version)
{
        s32 ret = -1;
        u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};

        ret = gtp_i2c_read(client, buf, sizeof(buf));
        if (ret < 0)
        {
                printk("GTP read version failed");
                return ret;
        }

        if (version)
        {
                *version = (buf[7] << 8) | buf[6];
        }

        if (buf[5] == 0x00)
        {
                printk("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
        }
        else
        {
                printk("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
        }

        return ret;
}

/*******************************************************
Function:
        I2c test Function.

Input:
        client:i2c client.

Output:
        Executive outcomes.0--success,non-0--fail.
*******************************************************/
static s8 gtp_i2c_test(struct i2c_client *client)
{
        u8 cmd_data[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
        u8 retry = 0;
        s8 ret = -1;

        while (retry++ < 2)
        {
                ret = gtp_i2c_read(client, cmd_data, 3);
                if (ret > 0)
                {
                        printk("test len %d data:%02x %02x %02x.\r\n", ret, cmd_data[0], cmd_data[1], cmd_data[2]);
                        return ret;
                }

                printk("GTP i2c test failed time %d.", retry);
        }

        return ret;
}

/*******************************************************
Function:
        Request input device Function.

Input:
        ts:private data.

Output:
        Executive outcomes.0--success,non-0--fail.
*******************************************************/
static char gtp_request_input_dev(struct goodix_ts_data *ts)
{
        char ret = -1;

        ts->input_dev = input_allocate_device();
        if (ts->input_dev == NULL)
        {
                GTP_ERROR("Failed to allocate input device.");
                return -ENOMEM;
        }

        ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
        ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

        set_bit(ABS_MT_POSITION_X, ts->input_dev->absbit);
        set_bit(ABS_MT_POSITION_Y, ts->input_dev->absbit);
        set_bit(ABS_MT_TOUCH_MAJOR, ts->input_dev->absbit);
        set_bit(ABS_MT_WIDTH_MAJOR, ts->input_dev->absbit);

        input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
        input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
        input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
        input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
        input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);
        __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);

        ts->input_dev->name = CTP_NAME;
        ts->input_dev->phys = "input/gt9xx_ts";
        ts->input_dev->id.bustype = BUS_I2C;
        ts->input_dev->id.vendor = 0xDEAD;
        ts->input_dev->id.product = 0xBEEF;
        ts->input_dev->id.version = 10427;
        ret = input_register_device(ts->input_dev);
        if (ret)
        {
                printk("Register %s input device failed", ts->input_dev->name);
                return -ENODEV;
        }

        return 0;
}

/*******************************************************
Function:
        Goodix touchscreen probe function.

Input:
        client:	i2c device struct.
        id:device id.

Output:
        Executive outcomes. 0---succeed.
*******************************************************/
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
        int ret = -1, gpio_pin = 0;
        u16 version_info;
        struct goodix_ts_data *ts;
        struct device_node *np = client->dev.of_node;

        printk("GTP Driver Version:%s\n", GTP_DRIVER_VERSION);
        printk("GTP I2C Address:0x%02x\n", client->addr);

        i2c_connect_client = client;
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
        {
                printk("I2C check functionality failed.\n");
                return -ENODEV;
        }

        ts = kzalloc(sizeof(*ts), GFP_KERNEL);
        if (ts == NULL)
        {
                printk("Alloc GFP_KERNEL memory failed.\n");
                return -ENOMEM;
        }
        memset(ts, 0, sizeof(*ts));

        INIT_WORK(&ts->work, goodix_ts_work_func);
        ts->client = client;

        i2c_set_clientdata(client, ts);
        ts->gtp_rawdiff_mode = 0;

        ret = gtp_i2c_test(client);
        if (ret < 0)
        {
                printk("I2C communication ERROR!\n");
                goto exit_device_detect;
        }

        gtp_read_version(client, &version_info);

        printk("create_singlethread_workqueue goodix_resume.\n");
        goodix_resume_wq = create_singlethread_workqueue("goodix_resume");
        if (goodix_resume_wq == NULL)
        {
                printk("create goodix_resume_wq fail!\n");
                return -ENOMEM;
        }

        printk(KERN_ALERT "create_singlethread_workqueue goodix_wq.\n");
        goodix_wq = create_singlethread_workqueue("goodix_wq");
        if (!goodix_wq)
        {
                printk("Creat goodix_wq workqueue failed.\n");
                return -ENOMEM;
        }

        ret = gtp_init_panel(ts);
        if (ret < 0)
        {
                printk("GTP init panel failed.\n");
        }

        ret = gtp_request_input_dev(ts);
        if (ret < 0)
        {
                printk("GTP request input dev failed\n");
                goto exit_device_detect;
        }
        config_info.dev = &(ts->input_dev->dev);

        spin_lock_init(&ts->irq_lock);

        gpio_pin = of_get_named_gpio(np, "gpio_int", 0); // gpio:89
        gpio_direction_input(gpio_pin);

        ts->use_irq = of_irq_get(np, 0); // irq:116
        ret = request_threaded_irq(ts->use_irq, NULL, goodix_ts_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, CTP_NAME, ts);
        if (ret)
        {
                printk("Request irq fail!.\n");
        }
        config_info.int_number = ts->use_irq;

        printk("ts->use_irq:%d, gpio:%d.\n", ts->use_irq, gpio_pin);

        printk("gt9xx probe success!\n");
        return 0;

exit_device_detect:
        i2c_set_clientdata(client, NULL);
        kfree(ts);

        return ret;
}

/*******************************************************
Function:
        Goodix touchscreen driver release function.

Input:
        client:	i2c device struct.

Output:
        Executive outcomes. 0---succeed.
*******************************************************/
static int goodix_ts_remove(struct i2c_client *client)
{
        struct goodix_ts_data *ts = i2c_get_clientdata(client);

        printk("%s start!\n", __func__);

        disable_irq_nosync(ts->use_irq);

        flush_workqueue(goodix_wq);
        cancel_work_sync(&goodix_resume_work);

        if (goodix_wq)
                destroy_workqueue(goodix_wq);

        if (goodix_resume_wq)
                destroy_workqueue(goodix_resume_wq);

        i2c_set_clientdata(ts->client, NULL);
        input_unregister_device(ts->input_dev);

        free_irq(ts->use_irq, ts);
        kfree(ts);

        return 0;
}

static void goodix_resume_events(struct work_struct *work)
{
        struct goodix_ts_data *ts = i2c_get_clientdata(i2c_connect_client);
        gtp_irq_enable(ts);
}

static const struct i2c_device_id goodix_ts_id[] = {
    {CTP_NAME, 0},
    {}};

static struct i2c_driver goodix_ts_driver = {
    .class = I2C_CLASS_HWMON,
    .probe = goodix_ts_probe,
    .remove = goodix_ts_remove,
    .id_table = goodix_ts_id,
    .driver = {
        .name = CTP_NAME,
        .owner = THIS_MODULE,
    },
    .address_list = normal_i2c,
};

static int ctp_get_system_config(void)
{
        config_info.input_type = CTP_TYPE;
        config_info.screen_max_x = 1280;
        config_info.screen_max_y = 800;
        config_info.revert_x_flag = 0;
        config_info.revert_y_flag = 0;
        config_info.exchange_x_y_flag = 0;

        ctp_print_info(config_info, DEBUG_INIT);

        screen_max_x = config_info.screen_max_x;
        screen_max_y = config_info.screen_max_y;
        revert_x_flag = config_info.revert_x_flag;
        revert_y_flag = config_info.revert_y_flag;
        exchange_x_y_flag = config_info.exchange_x_y_flag;

        if ((screen_max_x == 0) || (screen_max_y == 0))
        {
                printk("%s:read config error!\n", __func__);
                return 0;
        }

        return 1;
}

/*******************************************************
 * Function:
 *     Driver Install function.
 *     Input:
 *         None.
 *         Output:
 *             Executive Outcomes. 0---succeed.
 *             ********************************************************/
static int __init goodix_ts_init(void)
{
        s32 ret = -1;
        struct device_node *np = NULL;

        printk("GTP driver init...\n");
        np = of_find_node_by_name(NULL, "ctp");
        if (!np)
        {
                printk("ERROR! get ctp node failed, func:%s, line:%d\n", __FUNCTION__, __LINE__);
        }
        msleep(10);
        if (!ctp_get_system_config())
        {
                printk("%s:read config fail!\n", __func__);
                return ret;
        }

        goodix_ts_driver.detect = ctp_detect;
        ret = i2c_add_driver(&goodix_ts_driver);

        printk("GTP driver init finish.\n");

        return ret;
}

/*******************************************************
Function:
        Driver uninstall function.
Input:
  None.
Output:
        Executive Outcomes. 0---succeed.
********************************************************/
static void __exit goodix_ts_exit(void)
{
        printk("GTP driver exited.\n");
        i2c_del_driver(&goodix_ts_driver);
}

late_initcall(goodix_ts_init);
module_exit(goodix_ts_exit);

MODULE_DESCRIPTION("GTP Series Driver");
MODULE_LICENSE("GPL");
